
#include "myimage.h"
#pragma comment(lib, "msimg32")

// CMyImageStream

CMyImageStream::CMyImageStream(void* pData, DWORD dwSize)
: m_pData((BYTE *)pData), m_iCurPos(0), m_iSize(dwSize)
{
}

// IUnknown
STDMETHODIMP CMyImageStream::QueryInterface(REFIID riid, void **ppvObject)
{
	if (ppvObject == NULL)
		return E_INVALIDARG;

	*ppvObject = NULL;

	if (riid == __uuidof(IUnknown) || riid == __uuidof(IStream) || riid == __uuidof(ISequentialStream))
		*ppvObject = static_cast<IStream*>(this);
	else if (riid == __uuidof(IImageBytes))
		*ppvObject = static_cast<IImageBytes*>(this);

	if (*ppvObject)
		return S_OK;
	
	return E_NOINTERFACE; 
}

STDMETHODIMP_(ULONG) CMyImageStream::AddRef()
{ 
	return 1;
}

STDMETHODIMP_(ULONG) CMyImageStream::Release()
{
	return 1;
}

// ISequentialStream
STDMETHODIMP CMyImageStream::Read(void *pv, ULONG cb, ULONG *pcbRead)
{
	if (m_iCurPos + cb > m_iSize)
		cb = (ULONG)(m_iSize - m_iCurPos);

	memcpy(pv, m_pData + m_iCurPos, cb);

	m_iCurPos += cb;
	
	if (pcbRead)
		*pcbRead = cb;

	return 0;
}

STDMETHODIMP CMyImageStream::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
{
	switch (dwOrigin)
	{
	case STREAM_SEEK_SET:
		m_iCurPos = 0;
		break;

	case STREAM_SEEK_END:
		m_iCurPos = m_iSize;
		break;

	case STREAM_SEEK_CUR:
		break;
	}

	m_iCurPos += dlibMove.QuadPart;
	
	if (plibNewPosition)
		plibNewPosition->QuadPart = m_iCurPos;
	
	if (m_iCurPos < 0)
		m_iCurPos = 0;
	else if (m_iCurPos > m_iSize)
		m_iCurPos = m_iSize;
	
	return 0;
}

// IStream
STDMETHODIMP CMyImageStream::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
{
	pstatstg->cbSize.QuadPart = m_iSize;
	pstatstg->grfMode = STGM_SIMPLE;

	return 0;
}

STDMETHODIMP CMyImageStream::Write(const void *pv, ULONG cb, ULONG *pcbWritten)
{ 
	return 0;
}

STDMETHODIMP CMyImageStream::SetSize(ULARGE_INTEGER libNewSize)
{
	return 0;
}

STDMETHODIMP CMyImageStream::CopyTo(IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *pcbWritten)
{ 
	return 0;
}

STDMETHODIMP CMyImageStream::Commit(DWORD grfCommitFlags)
{
	return 0;
}

STDMETHODIMP CMyImageStream::Revert()
{
	return 0;
}

STDMETHODIMP CMyImageStream::LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{
	return 0;
}

STDMETHODIMP CMyImageStream::UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
{
	return 0;
}

STDMETHODIMP CMyImageStream::Clone(IStream **ppstm)
{
	return 0;
}

// IImageBytes
STDMETHODIMP CMyImageStream::CountBytes(UINT *pcb)
{
	*pcb = (UINT)m_iSize;

	return 0;
}

STDMETHODIMP CMyImageStream::LockBytes(UINT cb, ULONG ulOffset, const VOID **ppvBytes)
{
	*ppvBytes = m_pData + ulOffset;

	return 0;
}
STDMETHODIMP CMyImageStream::UnlockBytes(const VOID *pvBytes, UINT cb, ULONG ulOffset)
{
	return 0;
}

//////////////////////////////////////////////////////////////////////////
// CMyImage

CMyImage::CMyImage() : m_bNotAlphaImage(true), m_hBitmap(NULL)
{
	m_szBitmap.cx = 0;
	m_szBitmap.cy = 0;

	HDC hDC = GetWindowDC(::GetDesktopWindow());
	m_hBufferDC = ::CreateCompatibleDC(hDC);
	
	ReleaseDC(::GetDesktopWindow(), hDC);
}

CMyImage::~CMyImage()
{
	if (m_hBitmap)
	{
		::DeleteObject(m_hBitmap);
	}

	if (m_hBufferDC)
	{
		DeleteDC(m_hBufferDC);
	}
}

bool CMyImage::LoadFromFile(LPCTSTR lpszFileName)
{
	Gdiplus::Bitmap *pBitmap = Gdiplus::Bitmap::FromFile(lpszFileName);
	if (pBitmap)
	{
		if (pBitmap->GetLastStatus() == Gdiplus::Ok)
		{
			if (m_hBitmap)
				::DeleteObject(m_hBitmap);
			pBitmap->GetHBITMAP(NULL, &m_hBitmap);
		}

		BITMAP bmpData;
		::GetObject(m_hBitmap, sizeof(BITMAP), &bmpData);
		m_szBitmap.cx = bmpData.bmWidth;
		m_szBitmap.cy = bmpData.bmHeight;

		m_bNotAlphaImage = bmpData.bmBitsPixel < 32;

		delete pBitmap;

		return true;
	}
	
	return false;
}

void CMyImage::LoadFromData(void *pData, DWORD dwSize)
{
	CMyImageStream stream(pData, dwSize);
	Gdiplus::Bitmap bmp(&stream);
	bmp.GetHBITMAP(Gdiplus::Color(0xFF, 0xFF, 0xFF), &m_hBitmap);

	BITMAP bmpData;
	::GetObject(m_hBitmap, sizeof(BITMAP), &bmpData);
	m_szBitmap.cx = bmpData.bmWidth;
	m_szBitmap.cy = bmpData.bmHeight;	

	m_bNotAlphaImage = true;
}

void CMyImage::Destroy()
{
	if (m_hBitmap)
	{
		::DeleteObject(m_hBitmap);
		m_hBitmap = NULL;
	}
}

// Draw
BOOL CMyImage::Draw(HDC hDestDC, int xDest, int yDest, int nDestWidth, int nDestHeight, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight) const
{
	::SelectObject(m_hBufferDC, m_hBitmap);

	if (m_bNotAlphaImage)
		return ::StretchBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight, m_hBufferDC, xSrc, ySrc, nSrcWidth, nSrcHeight, SRCCOPY);

	BLENDFUNCTION bf;
	bf.BlendOp = AC_SRC_OVER;
	bf.BlendFlags = 0;
	bf.SourceConstantAlpha = 0xff;
	bf.AlphaFormat = AC_SRC_ALPHA;
	return ::AlphaBlend(hDestDC, xDest, yDest, nDestWidth, nDestHeight, m_hBufferDC, xSrc, ySrc, nSrcWidth, nSrcHeight, bf);
}

BOOL CMyImage::Draw(HDC hDestDC) const
{
	return Draw(hDestDC, 0, 0, m_szBitmap.cx, m_szBitmap.cy, 0, 0, m_szBitmap.cx, m_szBitmap.cy);
}

BOOL CMyImage::Draw(HDC hDestDC, const RECT &rectDest, const RECT &rectSrc) const
{
	return Draw(hDestDC, rectDest.left, rectDest.top, rectDest.right - rectDest.left, rectDest.bottom - rectDest.top, rectSrc.left, rectSrc.top, rectSrc.right - rectSrc.left, rectSrc.bottom - rectSrc.top);
}

BOOL CMyImage::Draw(HDC hDestDC, int xDest, int yDest, int nDestWidth, int nDestHeight, int xSrc, int ySrc) const
{
	return Draw(hDestDC, xDest, yDest, nDestWidth, nDestHeight, xSrc, ySrc, nDestWidth, nDestHeight);
}

// Bitblt
BOOL CMyImage::BitBlt(HDC hDestDC, int xDest, int yDest, int nDestWidth, int nDestHeight, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight) const
{
	::SelectObject(m_hBufferDC, m_hBitmap);
	return ::StretchBlt(hDestDC, xDest, yDest, nDestWidth, nDestHeight, m_hBufferDC, xSrc, ySrc, nSrcWidth, nSrcHeight, SRCCOPY);
}

BOOL CMyImage::BitBlt(HDC hDestDC, const RECT &rectDest, const RECT &rectSrc) const
{
	return BitBlt(hDestDC, rectDest.left, rectDest.top, rectDest.right - rectDest.left, rectDest.bottom - rectDest.top, rectSrc.left, rectSrc.top, rectSrc.right - rectSrc.left, rectSrc.bottom - rectSrc.top);
}